﻿// -------------------------------------------------------------------------
//    @FileName         :    NFCityMapUtil.cpp
//    @Author           :    Gao.Yi
//    @Date             :    2025-09-17
//    @Email			:    445267987@qq.com
//    @Module           :    NFCityMapUtil
//    @Desc             :
//
// -------------------------------------------------------------------------

#include "NFCityMapUtil.h"

#include "NFCityMapInst.h"
#include "NFCityNormalViewFunc.h"
#include "NFCityNpc.h"
#include "NFCityPacketSendUtil.h"
#include "NFCityPlayer.h"

int NFCityMapUtil::LeaveMap(NFCityWorldObject* pWorldObject)
{
    CHECK_NULL(0, pWorldObject);

    NFCityMapInst* pMapInst = NFCityMapInst::GetObjByGlobalId(pWorldObject->GetMapInstId());
    if (!pMapInst)
    {
        return 0;
    }

    NFCityNeighborGrid oGrids;
    pMapInst->GetNeighborGrids(pWorldObject, oGrids);
    InteractNeigborForLeaveView(pWorldObject, oGrids);

    pMapInst->LeaveMap(pWorldObject);

    return 0;
}

int NFCityMapUtil::EnterMap(NFCityWorldObject* pWorldObject, NFCityMapInst* pMapInst, const Proto_Vector3& oEnterPos, int iDirection)
{
    CHECK_NULL(0, pWorldObject);
    CHECK_NULL(0, pMapInst);

    NFCityMapInst* pLeaveMapInst = NFCityMapInst::GetObjByGlobalId(pWorldObject->GetMapInstId());
    if (pLeaveMapInst)
    {
        LeaveMap(pWorldObject);
    }

    if (pWorldObject->IsPlayer())
    {
        NFCityPlayer* pPlayer = dynamic_cast<NFCityPlayer*>(pWorldObject);
        CHECK_NULL(0, pPlayer);
        auto pMapDesc = CityMapDesc::Instance()->GetDesc(pMapInst->GetMapDescId());
        CHECK_NULL(0, pMapDesc);

        if (pMapDesc->map_type == proto_ff::EN_MAP_TYPE_GUILD_BATTLE)
        {
            pPlayer->GetViewList()->SetMaxViewNum(MAX_GB_VIEW_PLAYER_NUM);
            pPlayer->GetViewList()->SetGBView(true);
        }
        else
        {
            pPlayer->GetViewList()->SetMaxViewNum(MAX_NV_VIEW_PLAYER_NUM);
            pPlayer->GetViewList()->SetGBView(false);
        }
    }

    int iRetCode = pMapInst->EnterMap(pWorldObject, oEnterPos, iDirection);
    CHECK_ERR(0, iRetCode, "enter map failed");

    //notify enter map rsp
    if (pWorldObject->IsPlayer())
    {
        NotifyMapEnterRsp(dynamic_cast<NFCityPlayer*>(pWorldObject));
    }

    //enter view
    NFCityNeighborGrid oGrids;
    pMapInst->GetNeighborGrids(pWorldObject, oGrids);
    InteractNeigborForEnterView(pWorldObject, oGrids);

    return 0;
}

int NFCityMapUtil::EnterMap(NFCityWorldObject* pWorldObject, NFCityMapInst* pMapInst, const Proto_Vector3& oEnterPos, int iDirection, bool bNotifyEnterView)
{
    CHECK_NULL(0, pWorldObject);
    CHECK_NULL(0, pMapInst);

    NFCityMapInst* pLeaveMapInst = NFCityMapInst::GetObjByGlobalId(pWorldObject->GetMapInstId());
    if (pLeaveMapInst)
    {
        LeaveMap(pWorldObject);
    }

    //notify enter map rsp
    if (pWorldObject->IsPlayer())
    {
        NFCityPlayer* pPlayer = dynamic_cast<NFCityPlayer*>(pWorldObject);
        auto pMapDesc = CityMapDesc::Instance()->GetDesc(pMapInst->GetMapDescId());
        CHECK_NULL(pPlayer->GetRoleID(), pMapDesc);

        if (pMapDesc->map_type == proto_ff::EN_MAP_TYPE_GUILD_BATTLE)
        {
            pPlayer->GetViewList()->SetMaxViewNum(MAX_GB_VIEW_PLAYER_NUM);
            pPlayer->GetViewList()->SetGBView(true);
        }
        else
        {
            pPlayer->GetViewList()->SetMaxViewNum(MAX_NV_VIEW_PLAYER_NUM);
            pPlayer->GetViewList()->SetGBView(false);
        }
    }

    int iRetCode = pMapInst->EnterMap(pWorldObject, oEnterPos, iDirection);
    CHECK_RET(iRetCode, "enter map failed");

    //enter view
    if (bNotifyEnterView)
    {
        NFCityNeighborGrid oGrids;
        pMapInst->GetNeighborGrids(pWorldObject, oGrids);
        InteractNeigborForEnterView(pWorldObject, oGrids);
    }

    return 0;
}

int NFCityMapUtil::InteractNeigborForEnterView(NFCityWorldObject* pActiveObject, NFCityNeighborGrid& oGrids)
{
    CHECK_NULL(0, pActiveObject);

    auto pMapDesc = CityMapDesc::Instance()->GetDesc(pActiveObject->GetMapDescId());
    CHECK_NULL(0, pMapDesc);

    if (pMapDesc->map_type == proto_ff::EN_MAP_TYPE_GUILD_BATTLE)
    {
        if (pActiveObject->IsPlayer())
        {
            /*CGBOtherEnterView oOtherEnterView( (CPlayer *)pActiveObject, &oGrids );
            oGrids.IterateGrids( &oOtherEnterView );
            oOtherEnterView.Send();

            CGBEnterViewOther oEnterViewOther( (CPlayer *)pActiveObject );
            oGrids.IterateGrids( &oEnterViewOther );*/
        }
        //active-object enter other view
        else if (pActiveObject->IsNpc())
        {
            /*CEnterViewOther oEnterViewOther( pActiveObject );
            oGrids.IterateGrids( &oEnterViewOther );*/
        }
    }
    else
    {
        if (pActiveObject->IsPlayer())
        {
            NFCityPlayer* pActivePlayer = dynamic_cast<NFCityPlayer*>(pActiveObject);
            CHECK_NULL(0, pActivePlayer);
            NFNVOtherEnterView oOtherEnterView(pActivePlayer, &oGrids);
            oGrids.IterateGrids(&oOtherEnterView);
            oOtherEnterView.Send();

            NFNVEnterViewOther oEnterViewOther(pActivePlayer);
            oGrids.IterateGrids(&oEnterViewOther);

            //InteractTeamForEnterView(pActivePlayer, oGrids);
        }
        //active-object enter other view
        else if (pActiveObject->IsNpc())
        {
            NFEnterViewOther oEnterViewOther(pActiveObject);
            oGrids.IterateGrids(&oEnterViewOther);
        }
    }

    return 0;
}

int NFCityMapUtil::MakeRefreshView(NFCityWorldObject* pWorldObject, proto_ff::Proto_CSMapRefreshViewNtf* pRefreshViewNtf)
{
    CHECK_NULL(0, pWorldObject);
    CHECK_NULL(0, pRefreshViewNtf);

    proto_ff::CSViewOne* pViewOne = pRefreshViewNtf->add_view_list();
    CHECK_NULL(0, pViewOne);

    pViewOne->set_direction(pWorldObject->GetDirection());
    pWorldObject->GetPosition()->ToPb(pViewOne->mutable_position());

    switch (pWorldObject->GetObjectType())
    {
        case EN_CITY_WOT_PLAYER:
            MakePlayerEnterView(dynamic_cast<NFCityPlayer*>(pWorldObject), pViewOne);
            break;
        case EN_CITY_WOT_NPC:
            MakeNpcEnterView(dynamic_cast<NFCityNpc*>(pWorldObject), pViewOne);
            break;
        default:
            break;
    }

    return 0;
}

int NFCityMapUtil::MakeEnterView(NFCityWorldObject* pWorldObject, proto_ff::Proto_CSEnterViewNtf* pEnterViewNtf)
{
    CHECK_NULL(0, pWorldObject);
    CHECK_NULL(0, pEnterViewNtf);

    proto_ff::CSViewOne* pViewOne = pEnterViewNtf->add_view_list();
    CHECK_NULL(0, pViewOne);

    pViewOne->set_direction(pWorldObject->GetDirection());
    pWorldObject->GetPosition()->ToPb(pViewOne->mutable_position());

    switch (pWorldObject->GetObjectType())
    {
        case EN_CITY_WOT_PLAYER:
            MakePlayerEnterView(dynamic_cast<NFCityPlayer*>(pWorldObject), pViewOne);
            break;
        case EN_CITY_WOT_NPC:
            MakeNpcEnterView(dynamic_cast<NFCityNpc*>(pWorldObject), pViewOne);
            break;
        default:
            break;
    }

    return 0;
}

int NFCityMapUtil::MakeLeaveView(NFCityWorldObject* pWorldObject, proto_ff::Proto_CSLeaveViewNtf* pLeaveViewNtf)
{
    CHECK_NULL(0, pWorldObject);
    CHECK_NULL(0, pLeaveViewNtf);

    pLeaveViewNtf->add_view_list(pWorldObject->GetGlobalId());

    return 0;
}

int NFCityMapUtil::MakeLastAction(NFCityWorldObject* pWorldObject, bool& bIsAction, proto_ff::Proto_CSMapActionNtf* pActionNtf)
{
    bIsAction = false;

    CHECK_NULL(0, pWorldObject);

    if (pWorldObject->IsPlayer())
    {
        NFCityPlayer* pPlayer = dynamic_cast<NFCityPlayer*>(pWorldObject);
        CHECK_NULL(0, pPlayer);
        NFCityPlayerMoveExt* pMoveExt = pPlayer->GetMoveExt();
        if (pMoveExt->IsDance())
        {
            Proto_PvpSynAction stLastAction = *pMoveExt->GetLastAction();
            Proto_Vector3 oCurPos = *pWorldObject->GetPosition();;
            stLastAction.Position.x = oCurPos.x;
            stLastAction.Position.y = oCurPos.y;
            stLastAction.Position.z = oCurPos.z;
            pActionNtf->set_obj_id(pWorldObject->GetGlobalId());
            stLastAction.ToPb(pActionNtf->mutable_action());

            bIsAction = true;
        }
        else if (pMoveExt->IsAction())
        {
            Proto_PvpSynAction stLastAction = *pMoveExt->GetLastAction();
            Proto_Vector3 oCurPos = *pWorldObject->GetPosition();
            stLastAction.Position.x = oCurPos.x;
            stLastAction.Position.y = oCurPos.y;
            stLastAction.Position.z = oCurPos.z;
            stLastAction.synType = JUMP;
            pActionNtf->set_obj_id(pWorldObject->GetGlobalId());
            stLastAction.ToPb(pActionNtf->mutable_action());

            bIsAction = true;
        }
    }

    return 0;
}

int NFCityMapUtil::MakePlayerEnterView(NFCityPlayer* pPlayer, proto_ff::CSViewOne* pViewOne)
{
    NFCityPlayerMoveExt* pMoveExt = pPlayer->GetMoveExt();
    const Proto_PvpSynAction* pAction = pMoveExt->GetLastAction();

    pViewOne->set_id(pPlayer->GetGlobalId());
    pViewOne->set_obj_type(proto_ff::EN_ACT_TYPE_PLAYER_HERO);
    pViewOne->set_action(pAction->Action);
    pViewOne->set_start_frame(pAction->StartFrame);

    proto_ff::BattlePlayerHero* pPlayerHero = pViewOne->mutable_hero();
    CHECK_NULL(0, pPlayerHero);
    pPlayerHero->set_name(pPlayer->GetRoleName());
    pPlayerHero->set_role_id(pPlayer->GetRoleID());
    pPlayerHero->set_obj_id(pPlayer->GetGlobalId());
    pPlayerHero->set_desc_id(pPlayer->GetCityHero());
    pPlayerHero->set_camp(proto_ff::EN_CAMP_TYPE_BLUE);
    pPlayerHero->set_level(pPlayer->GetLevel());

    proto_ff::CSAttr* pAttr = pPlayerHero->mutable_attr_list()->add_attr_list();
    pAttr->set_type(proto_ff::EN_ATTR_MOV_SPD);
    pAttr->set_value(pPlayer->GetAttrValue(proto_ff::EN_ATTR_CITY_MOV_SPD));

    bool bIsTeamLeader = pPlayer->GetTeamExt()->IsTeamLeader(pPlayer->GetRoleID());
    pPlayerHero->set_is_team_leader(bIsTeamLeader);
    pPlayerHero->set_title_id(pPlayer->GetTitleID());
    pPlayerHero->set_title_name(pPlayer->GetTitleName());
    pPlayerHero->set_title_item_id(pPlayer->GetTitleItemID());
    pPlayerHero->set_title2_id(pPlayer->GetTitle2ID());
    pPlayerHero->set_guild_id(pPlayer->GetGuildID());
    pPlayerHero->set_in_battle(pPlayer->GetBattleExt()->IsInBattle());
    pPlayerHero->set_team_member_num(pPlayer->GetTeamExt()->GetTeamMemberNum());
    pPlayerHero->set_is_team_follow(pPlayer->GetTeamExt()->IsTeamFollow(pPlayer->GetRoleID()));
    pPlayerHero->set_equip_strength_level(pPlayer->GetEquipStrengthLevel());
    pPlayerHero->set_state_leave(pPlayer->IsStateLeave());
    pPlayerHero->set_transfer_id(pPlayer->GetTransferID());
    pPlayerHero->set_follow_partner_id(pPlayer->GetFollowPartnerID());
    pPlayerHero->set_follow_partner_fatigue(pPlayer->IsFollowPartnerFatigue());
    pPlayerHero->set_follow_partner_star(pPlayer->GetFollowPartnerStar());
    pPlayerHero->set_weapon_strengthen_lv(pPlayer->GetWeaponStrengLv());

    pPlayer->MakeFashionData(pPlayerHero);
    pPlayer->GetBuffExt()->MakeBuffData(pPlayerHero);
    pPlayer->MakeTransExpRole(pPlayerHero->mutable_trans_exp_data());

    return 0;
}

int NFCityMapUtil::MakeNpcEnterView(NFCityNpc* pNpc, proto_ff::CSViewOne* pViewOne)
{
    CHECK_NULL(0, pNpc);
    pViewOne->set_id(pNpc->GetGlobalId());
    pViewOne->set_obj_type(proto_ff::EN_ACT_TYPE_NPC);

    proto_ff::Npc* pNpcView = pViewOne->mutable_npc();
    CHECK_NULL(0, pNpcView);

    pNpcView->set_obj_id(pNpc->GetGlobalId());
    pNpcView->set_npc_id(pNpc->GetNpcID());
    pNpcView->set_camp((proto_ff::enGuildCampType)pNpc->GetNpcCamp());

    return 0;
}

int NFCityMapUtil::SynAction(NFCityWorldObject* pActiveObject, NFCityPlayer* pAcceptPlayer)
{
    CHECK_NULL(0, pAcceptPlayer);
    CHECK_NULL(0, pActiveObject);

    if (!pAcceptPlayer->IsEnterSvr())
        return 0;

    bool bIsAction = false;
    proto_ff::Proto_CSRsp oCSRsp;

    MakeLastAction(pActiveObject, bIsAction, oCSRsp.mutable_map_action_ntf());

    if (bIsAction)
    {
        NFCityPacketSendUtil::SendToClient(pAcceptPlayer, proto_ff::E_CS_MAP_ACTION_NTF, &oCSRsp);
    }

    return 0;
}

int NFCityMapUtil::InteractNeigborForLeaveView(NFCityWorldObject* pActiveObject, NFCityNeighborGrid& oGrids)
{
    CHECK_NULL(0, pActiveObject);

    //other enter active-object view
    if (pActiveObject->IsPlayer())
    {
        NFNVOtherLeaveView oOtherLeaveView(dynamic_cast<NFCityPlayer*>(pActiveObject));
        oGrids.IterateGrids(&oOtherLeaveView);
        oOtherLeaveView.Send();

        NFNVLeaveViewOther oLeaveViewOther(dynamic_cast<NFCityPlayer*>(pActiveObject));
        oGrids.IterateGrids(&oLeaveViewOther);
    }
    else if (pActiveObject->IsNpc())
    {
        NFLeaveViewOther oLeaveViewOther(pActiveObject);
        oGrids.IterateGrids(&oLeaveViewOther);
    }

    return 0;
}

int NFCityMapUtil::RefreshNeighborView(NFCityWorldObject* pActiveObject, NFCityNeighborGrid& oBeginGrids, NFCityNeighborGrid& oEndGrids)
{
    CHECK_NULL(0, pActiveObject);

    NFCityNeighborGrid oEnterGrids;
    NFCityNeighborGrid oLeaveGrids;
    oEndGrids.IntersectGrids(oBeginGrids, oEnterGrids, oLeaveGrids);

    if (oLeaveGrids.GetGridNum() > 0)
    {
        InteractNeigborForLeaveView(pActiveObject, oLeaveGrids);
    }

    if (oBeginGrids.GetGridNum() > 0)
    {
        InteractNeigborForEnterView(pActiveObject, oEnterGrids);
    }

    return 0;
}

int NFCityMapUtil::RefreshNeighborView(NFCityWorldObject* pActiveObject, NFCityMapInst* pMapInst, const Proto_Vector3& oEndPos)
{
    CHECK_NULL(0, pActiveObject);

    NFCityNeighborGrid oBeginGrids;
    pMapInst->GetNeighborGrids(pActiveObject, oBeginGrids);

    pMapInst->LeaveMap(pActiveObject);

    pMapInst->EnterMap(pActiveObject, oEndPos, 1);

    NFCityNeighborGrid oEndGrids;
    pMapInst->GetNeighborGrids(pActiveObject, oEndGrids);

    RefreshNeighborView(pActiveObject, oBeginGrids, oEndGrids);

    return 0;
}

int NFCityMapUtil::BroadCastNeighbor(NFCityWorldObject* pActiveObject, NFCityNeighborGrid& oGrids, int iCmd, google::protobuf::Message* pMessage)
{
    CHECK_NULL(0, pActiveObject);
    CHECK_NULL(0, pMessage);

    NFWorldObjectNotifyMessage oNotifyMessage(pActiveObject, iCmd, pMessage);
    oGrids.IterateGrids(&oNotifyMessage);

    return 0;
}

int NFCityMapUtil::BroadCastViewNeighbor(NFCityPlayer* pPlayer, int iCmd, const google::protobuf::Message* pMessage)
{
    CHECK_NULL(0, pPlayer);

    NFWorldObjectNotifyMessage oNotifyMessage(pPlayer, iCmd, pMessage);
    pPlayer->GetViewList()->IteratorEnterViewList(&oNotifyMessage);

    return 0;
}


int NFCityMapUtil::NotifyMapEnterRsp(NFCityPlayer* pPlayer)
{
    const RoleMapData* pstRoleMapData = pPlayer->GetMapData();

    proto_ff::Proto_CSRsp oCSRsp;
    proto_ff::Proto_CSMapEnterRsp* pMapEnterRsp = oCSRsp.mutable_map_enter_rsp();
    CHECK_NULL(0, pMapEnterRsp);

    pMapEnterRsp->set_result(0);
    pstRoleMapData->ToPb(pMapEnterRsp->mutable_map_data());
    pMapEnterRsp->set_obj_id(pPlayer->GetGlobalId());

    //make view player
    proto_ff::CSViewOne* pViewOne = pMapEnterRsp->mutable_self_view();
    CHECK_NULL(0, pViewOne);

    pViewOne->set_direction(pPlayer->GetDirection());
    pPlayer->GetPosition()->ToPb(pViewOne->mutable_position());

    MakePlayerEnterView(pPlayer, pViewOne);

    NFCityPacketSendUtil::SendToClient(pPlayer, proto_ff::E_CS_MAP_ENTER_RSP, &oCSRsp);

    return 0;
}

int NFCityMapUtil::NotifyMapLeaveRsp(NFCityPlayer* pPlayer)
{
    proto_ff::Proto_CSRsp oCSRsp;
    proto_ff::Proto_CSMapLeaveRsp* pMapLeaveRsp = oCSRsp.mutable_map_leave_rsp();
    CHECK_NULL(0, pMapLeaveRsp);

    pMapLeaveRsp->set_result(0);

    NFCityPacketSendUtil::SendToClient(pPlayer, proto_ff::E_CS_MAP_LEAVE_RSP, &oCSRsp);

    return 0;
}

int NFCityMapUtil::ResetPos(NFCityWorldObject* pWo)
{
    CHECK_NULL(0, pWo);
    LOG_DEBUG(0, "reset pos for obj:{}", pWo->GetGlobalId());

    if (pWo->IsPlayer())
    {
        auto pPlayer = dynamic_cast<NFCityPlayer*>(pWo);
        CHECK_NULL(0, pPlayer);
        pPlayer->GetMoveExt()->ClearLastAction();
    }

    proto_ff::Proto_CSRsp oCSRsp;
    proto_ff::Proto_CSMapPosResetNtf* pMapPosResetNtf = oCSRsp.mutable_map_pos_reset_ntf();
    proto_ff::Proto_CSActorMoveReset* pMoveReset = pMapPosResetNtf->mutable_move_reset();
    pMoveReset->set_id(pWo->GetGlobalId());
    pWo->GetPosition()->ToPb(pMoveReset->mutable_pos());
    pMoveReset->set_platform_id(0);
    pMoveReset->set_reason(proto_ff::E_MoveReset_Reason_None);

    NFCityMapInst* pMapInst = NFCityMapInst::GetObjByGlobalId(pWo->GetMapInstId());
    CHECK_NULL(pWo->GetMapInstId(), pMapInst);

    //notify all include self
    NFCityNeighborGrid oGrids;
    pMapInst->GetNeighborGrids(pWo, oGrids);
    NFWorldObjectNotifyMessage oNotifyMessage(nullptr, proto_ff::E_CS_MAP_MOVE_RESET_NTF, &oCSRsp);
    oGrids.IterateGrids(&oNotifyMessage);
    return 0;
}
